package com.gzz.common.base;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.gzz.common.base.annotation.Exclude;
import com.gzz.common.base.annotation.Id;
import com.gzz.common.base.annotation.Table;

import lombok.extern.slf4j.Slf4j;

/**
 * @功能描述 【数据访问】基类本类中的方法使用反射方式执行,推荐在不影影性能的地方使用
 * @author 高振中
 * @date 2021-02-01 20:20:20
 */
@Slf4j
@Scope("prototype")
public class BaseDao extends BaseJDBC {

	@Autowired
	private HttpServletRequest request;

	public <T, C extends BaseCondition> int count(Class<T> clazz, C cond) {
		return count(false, clazz, cond);
	}

	public <T, C extends BaseCondition> int count(boolean sql, Class<T> clazz, C cond) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		if (includeDr(fields))
			cond.setDr(Byte.valueOf("0"));
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT COUNT(1) FROM ");
		sb.append(tables[0].value());
		sb.append(" t");
		sb.append(cond.where());
		if (sql)
			log.info(sql(sb.toString(), cond.array()));
		return count(sb.toString(), cond);
	}

	public <T, C extends BaseCondition> Page<T> page(Class<T> clazz, C cond) {
		return page(false, clazz, cond);
	}

	private static Boolean includeDr(List<Field> fields) {
		return fields.stream().filter(i -> i.getName().equals("dr")).collect(Collectors.toList()).size() > 0;
	}

	public <T, C extends BaseCondition> Page<T> page(boolean sql, Class<T> clazz, C cond) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		if (includeDr(fields))
			cond.setDr(Byte.valueOf("0"));
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT ");
		sb.append(Joiner.on(",").join(fields.stream().map(i -> "t." + i.getName()).collect(Collectors.toList())));
		sb.append(" FROM ");
		sb.append(tables[0].value());
		sb.append(" t");
		sb.append(cond.where());
		sb.append(cond.getOrders());
		if (sql)
			log.info(sql(sb.toString(), cond.array()));
		return page(sb.toString(), cond, clazz);
	}

	public <T> T findById(Class<T> clazz, Object obj) {
		return findById(false, clazz, obj);
	}

	public <T> T findById(boolean sql, Class<T> clazz, Object obj) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		List<Field> id = fields.stream().filter(i -> i.getDeclaredAnnotationsByType(Id.class).length > 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT ");
		sb.append(Joiner.on(",").join(fields.stream().map(i -> "t." + i.getName()).collect(Collectors.toList())));
		sb.append(" FROM ");
		sb.append(tables[0].value());
		sb.append(" t WHERE t.");
		sb.append(id.get(0).getName());
		sb.append(" = ?");
		if (sql)
			log.info(sql(sb.toString(), obj));
		return findById(sb.toString(), clazz, obj);
	}

	public <T, C extends BaseCondition> List<T> list(Class<T> clazz, C cond) {
		return list(false, clazz, cond);
	}

	public <T, C extends BaseCondition> List<T> list(boolean sql, Class<T> clazz, C cond) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		if (includeDr(fields))
			cond.setDr(Byte.valueOf("0"));
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT ");
		sb.append(Joiner.on(",").join(fields.stream().map(i -> "t." + i.getName()).collect(Collectors.toList())));
		sb.append(" FROM ");
		sb.append(tables[0].value());
		sb.append(" t ");
		sb.append(cond.where());
		sb.append(cond.getOrders());
		if (sql)
			log.info(sql(sb.toString(), cond.array()));
		return list(sb.toString(), cond, clazz);
	}

	public <T> int[] saveBatch(Class<T> clazz, List<T> list) {
		return saveBatch(false, clazz, list);
	}

	public <T> int[] saveBatch(boolean sql, Class<T> clazz, List<T> list) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("INSERT INTO ");
		sb.append(tables[0].value());
		sb.append(" (" + Joiner.on(",").join(fields.stream().map(i -> i.getName()).collect(Collectors.toList())) + ")");
		sb.append(" VALUES (" + Joiner.on(",").join(fields.stream().map(i -> ":" + i.getName()).collect(Collectors.toList())) + ")");
		if (sql)
			log.info(sqlp(sb.toString(), list));
		return batchOperate(list, sb.toString());
	}

	public <T> int save(T t) {
		return save(false, t);
	}

	public <T> int save(boolean sql, T t) {
		Class<?> clazz = t.getClass();
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("INSERT INTO ");
		sb.append(tables[0].value());
		sb.append(" (" + Joiner.on(",").join(fields.stream().map(i -> i.getName()).collect(Collectors.toList())) + ")");
		sb.append(" VALUES (" + Joiner.on(",").join(fields.stream().map(i -> "?").collect(Collectors.toList())) + ")");
		Object[] array = fields.stream().map(i -> {
			try {
				i.setAccessible(true);
				if (i.getName().equals("createTime"))
					return LocalDateTime.now();
				if (i.getName().equals("createBy"))
					return request.getAttribute("userId");
				if (i.getName().equals("dr") && i.get(t) == null)
					return Byte.valueOf("0");

				return i.get(t);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			return null;
		}).collect(Collectors.toList()).toArray();
		if (sql)
			log.info(sql(sb.toString(), array));
		return update(sb.toString(), array);
	}

	public <T> int delete(Class<T> clazz, Object... ids) {
		return delete(false, clazz, ids);
	}

	public <T> int delete(boolean sql, Class<T> clazz, Object... ids) {
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		List<Field> id = fields.stream().filter(i -> i.getDeclaredAnnotationsByType(Id.class).length > 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		if (includeDr(fields))
			sb.append("UPDATE " + tables[0].value() + " SET dr=1");
		else
			sb.append("DELETE FROM " + tables[0].value());
		sb.append(" WHERE ");
		sb.append(id.get(0).getName());
		sb.append(" IN");
		sb.append(toIn(ids));
		if (sql)
			log.info(sql(sb.toString(), ids));
		return update(sb.toString(), ids);
	}

	public <T, C extends BaseCondition> int delete(Class<T> clazz, C cond) {
		return delete(false, clazz, cond);
	}

	public <T, C extends BaseCondition> int delete(boolean sql, Class<T> clazz, C cond) {
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("DELETE FROM ");
		sb.append(tables[0].value() + " t");
		sb.append(cond.where());
		if (sql)
			log.info(sql(sb.toString(), cond.array()));
		return update(sb.toString(), cond.array());
	}

	public <T> int update(T t) {
		return update(false, t);
	}

	public <T> int update(boolean sql, T t) {
		Class<?> clazz = t.getClass();
		List<Field> fields = Lists.newArrayList(clazz.getDeclaredFields()).stream().filter(i -> i.getDeclaredAnnotationsByType(Exclude.class).length == 0).collect(Collectors.toList());
		List<Field> ids = fields.stream().filter(i -> i.getDeclaredAnnotationsByType(Id.class).length > 0).collect(Collectors.toList());
		List<Field> noIds = fields.stream().filter(i -> i.getDeclaredAnnotationsByType(Id.class).length == 0).collect(Collectors.toList());
		Table[] tables = clazz.getDeclaredAnnotationsByType(Table.class);
		StringBuffer sb = new StringBuffer();
		sb.append("UPDATE ");
		sb.append(tables[0].value());
		sb.append(" SET " + Joiner.on(",").join(noIds.stream().map(i -> i.getName() + "=?").collect(Collectors.toList())));
		sb.append(" WHERE ");
		sb.append(ids.get(0).getName() + "=?");
		noIds.addAll(ids);
		Object[] array = noIds.stream().map(i -> {
			try {
				i.setAccessible(true);
				if (i.getName().equals("updateTime"))
					return LocalDateTime.now();
				if (i.getName().equals("updateBy"))
					return request.getAttribute("userId");
				return i.get(t);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			return null;
		}).collect(Collectors.toList()).toArray();
		if (sql)
			log.info(sql(sb.toString(), array));
		return update(sb.toString(), array);
	}

}